package io.renren.modules.mvs.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.renren.common.exception.RRException;
import io.renren.common.utils.Constant;
import io.renren.common.utils.MvsUtils;
import io.renren.common.utils.PageUtils;
import io.renren.modules.mvs.dao.MvsPaymentDao;
import io.renren.modules.mvs.dao.MvsPaymentDetailDao;
import io.renren.modules.mvs.dao.MvsTicketDao;
import io.renren.modules.mvs.dao.MvsViolationDao;
import io.renren.modules.mvs.dto.PaymentDetailDto;
import io.renren.modules.mvs.dto.TicketDto;
import io.renren.modules.mvs.entity.PaymentEntity;
import io.renren.modules.mvs.entity.PaymentDetailEntity;
import io.renren.modules.mvs.entity.TicketEntity;
import io.renren.modules.mvs.entity.ViolationEntity;
import io.renren.modules.mvs.service.PaymentDetailService;
import io.renren.modules.mvs.service.TicketService;
import io.renren.modules.sys.dao.SysRoleDao;
import io.renren.modules.sys.entity.SysRoleEntity;
import io.renren.modules.sys.entity.SysUserEntity;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static io.renren.common.utils.Constant.*;


@Service
public class TicketServiceImpl extends ServiceImpl<MvsTicketDao, TicketEntity> implements TicketService {
    @Autowired
    private SysRoleDao sysRoleDao;
    @Autowired
    private MvsPaymentDao mvsPaymentDao;
    @Autowired
    private MvsViolationDao mvsViolationDao;
    @Autowired
    private PaymentDetailService paymentDetailService;
    @Autowired
    private MvsPaymentDetailDao mvsPaymentDetailDao;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        long currPage = 1;
        long limit = 10;

        if (params.get(Constant.PAGE) != null) {
            currPage = Long.parseLong((String) params.get(Constant.PAGE));
        }
        if (params.get(Constant.LIMIT) != null) {
            limit = Long.parseLong((String) params.get(Constant.LIMIT));
        }

        SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
        Long userId = user.getUserId();
        List<SysRoleEntity> roles = sysRoleDao.queryListByUserId(userId);
        // 如果角色是机动车车主，则只能看到自己的罚单数据
        if (roles.size() == 1 && VEHICLE_OWNER.equals(roles.get(0).getRoleName())) {
            params.put("userId", userId);
        }
        IPage<TicketDto> page = this.baseMapper.selectPageByCondition(new Page<>(currPage, limit), params);
        List<TicketDto> records = page.getRecords();
        records.sort(Comparator.comparing(TicketDto::getCreateTime).reversed());

        //查询罚单下的缴费详情列表
        List<String> ticketIds = records.stream().map(TicketDto::getId).collect(Collectors.toList());
        List<PaymentDetailDto> paymentDetails = mvsPaymentDetailDao.selectListByTicketIds(ticketIds);
        //根据ticketId分组
        Map<String, List<PaymentDetailDto>> detailMap = paymentDetails.stream().collect(Collectors.groupingBy(PaymentDetailDto::getTicketId));
        records.forEach(item -> {
            List<PaymentDetailDto> temp = detailMap.get(item.getId());
            if (temp != null) {
                temp.sort(Comparator.comparing(PaymentDetailDto::getCreateTime).reversed());
                item.setPaymentDetailList(temp);
            }
        });

        return new PageUtils(page);
    }

    @Override
    public List<TicketDto> getTicketDtoList() {
        return this.baseMapper.selectTicketDtoList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BigDecimal payment(BigDecimal payMoney, String ticketId, Long userId) {
        if (payMoney.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal surplus = new BigDecimal(String.valueOf(payMoney));

        //1.查询该用户下所有未缴清费用的罚单，并按罚单产生时间排序
        List<TicketEntity> ticketList;
        if (StringUtils.isNotBlank(ticketId)) { // 如果罚单编号存在，则只对该编号对应的罚单进行缴费
            TicketEntity ticket = this.baseMapper.selectByWaitPayment(ticketId);
            ticketList = new ArrayList<>();
            if (ticket != null) {
                ticketList.add(ticket);
            }
        } else {
            ticketList = this.baseMapper.selectWaitPaymentList(userId);
        }
        if (ticketList.isEmpty()) {
            throw new RRException("缴费失败，您当前没有需要缴费的订单");
        }
        //2.按顺序对罚单进行缴费，先缴罚金，再缴滞纳金
        List<Long> violationIds = new ArrayList<>();    // 保存违章记录id
        List<PaymentDetailEntity> paymentLogList = new ArrayList<>();  // 保存每个罚单的缴纳记录
        for (TicketEntity ticket : ticketList) {
            if (surplus.compareTo(BigDecimal.ZERO) == 0) {
                break;
            }
            surplus = handleTicket(ticket, surplus, violationIds, userId, paymentLogList);
        }
        //3.将缴完费复罚单数据同步回数据库
        TicketService proxy = (TicketService) AopContext.currentProxy();
        proxy.updateBatchById(ticketList);  // 为防止this调用导致事务失效，使用当前对象的代理对象
        //4.将已缴清的罚单对应的违章记录设置为已处理状态
        if (!violationIds.isEmpty()) {
            ViolationEntity violation = new ViolationEntity();
            violation.setViolationStatus(PROCESSED);
            mvsViolationDao.update(violation, new LambdaQueryWrapper<ViolationEntity>()
                    .in(ViolationEntity::getId, violationIds));
        }
        //5.生成缴纳记录，并写到数据库中
        PaymentEntity paymentEntity = new PaymentEntity();
        paymentEntity.setUserId(userId);
        paymentEntity.setPaymentMoney(payMoney);
        paymentEntity.setPaymentTime(new Date());
        mvsPaymentDao.insert(paymentEntity);
        //6.将每个罚单的缴纳记录写到数据库
        paymentLogList.forEach(item -> item.setPaymentId(paymentEntity.getId()));
        paymentDetailService.saveBatch(paymentLogList);
        return surplus;
    }

    private static BigDecimal handleTicket(
            TicketEntity ticket, BigDecimal surplus, List<Long> violationIds,
            Long userId, List<PaymentDetailEntity> paymentDetailList)
    {
        BigDecimal start = new BigDecimal(String.valueOf(surplus));
        // 如果缴费金额小于等于待缴罚金
        if (surplus.compareTo(ticket.getWaitFine()) <= 0) {
            ticket.setWaitFine(ticket.getWaitFine().subtract(surplus));
            ticket.setTicketStatus(PARTIALLY_PAID); // 设置为部分缴清状态
            surplus = BigDecimal.ZERO;
        } else {    // 缴费金额大于待缴罚金
            surplus = surplus.subtract(ticket.getWaitFine());
            ticket.setWaitFine(BigDecimal.ZERO);    // 待缴罚金被缴清
            if (surplus.compareTo(ticket.getWaitOverdueFee()) < 0) {
                ticket.setWaitOverdueFee(ticket.getWaitOverdueFee().subtract(surplus));
                surplus = BigDecimal.ZERO;
            } else {
                surplus = surplus.subtract(ticket.getWaitOverdueFee());
                ticket.setWaitOverdueFee(BigDecimal.ZERO);
                // 待付滞纳金也被缴清，将罚单设置为已缴清状态
                ticket.setTicketStatus(FULLY_PAID);
                // 如果罚单为已缴清状态，则需要将违章记录设置为已处理
                violationIds.add(ticket.getViolationId());
            }
        }
        // 创建当前罚单的缴费记录
        PaymentDetailEntity paymentDetail = new PaymentDetailEntity();
        paymentDetail.setTicketId(ticket.getId());
        paymentDetail.setUserId(userId);
        paymentDetail.setPaymentMoney(start.subtract(surplus));
        paymentDetail.setTicketStatus(ticket.getTicketStatus());
        paymentDetail.setCreateTime(new Date());
        paymentDetailList.add(paymentDetail);
        return surplus;
    }

    @Override
    public TicketEntity getByWaitPayment(String id) {
        return this.baseMapper.selectByWaitPayment(id);
    }

    @Override
    public Map<String, Collection<?>> getFineListByDateRange(String startDate, String endDate) {
        if (StringUtils.isEmpty(startDate)) {
            // 如果startDate，去过去最近一周作为时间范围
            LocalDate end = LocalDate.now();
            LocalDate start = end.minusDays(6);
            startDate = start.toString();
            endDate = end.toString();
        } else if (StringUtils.isEmpty(endDate)) {
            // 如果endDate为空，则让endDate为当前时间
            LocalDate end = LocalDate.now();
            endDate = end.toString();
        }
        LocalDate start = LocalDate.parse(startDate);
        LocalDate end = LocalDate.parse(endDate);
        MvsUtils.checkDate(start, end);

        SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
        List<SysRoleEntity> roles = sysRoleDao.queryListByUserId(user.getUserId());
        // 如果角色是机动车车主，则只能看到自己的罚单数据
        if (roles.size() == 1 && VEHICLE_OWNER.equals(roles.get(0).getRoleName())) {
            List<Map<String, ?>> dbDataList = this.baseMapper.selectFineByDateRange(startDate, endDate, user.getUserId());
            return MvsUtils.getStatisticByDateRange(start, end, dbDataList);
        }
        List<Map<String, ?>> dbDataList = this.baseMapper.selectFineByDateRange(startDate, endDate, null);
        return MvsUtils.getStatisticByDateRange(start, end, dbDataList);
    }

    @Override
    public Map<String, Collection<?>> getMoneyListByDateRange(String startDate, String endDate) {
        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
            // 如果startDate或endDate为空，去过去最近一周作为时间范围
            LocalDate end = LocalDate.now();
            LocalDate start = end.minusDays(6);
            startDate = start.toString();
            endDate = end.toString();
        }
        LocalDate start = LocalDate.parse(startDate);
        LocalDate end = LocalDate.parse(endDate);
        MvsUtils.checkDate(start, end);

        SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
        List<SysRoleEntity> roles = sysRoleDao.queryListByUserId(user.getUserId());
        // 如果角色是机动车车主，则只能看到自己的罚单数据
        if (roles.size() == 1 && VEHICLE_OWNER.equals(roles.get(0).getRoleName())) {
            List<Map<String, ?>> dbDataList = this.baseMapper.selectMoneyByDateRange(startDate, endDate, user.getUserId());
            return MvsUtils.getStatisticByDateRange(start, end, dbDataList);
        }
        List<Map<String, ?>> dbDataList = this.baseMapper.selectMoneyByDateRange(startDate, endDate, null);
        return MvsUtils.getStatisticByDateRange(start, end, dbDataList);
    }

}